package com.objcat.servicegateway.security;

import java.io.UnsupportedEncodingException;
import java.util.Iterator;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.NettyDataBufferFactory;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authentication.ReactiveAuthenticationManagerAdapter;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.authentication.AuthenticationWebFilter;
import org.springframework.security.web.server.authentication.RedirectServerAuthenticationEntryPoint;
import org.springframework.web.server.WebFilter;
import com.alibaba.fastjson.JSONObject;
import io.netty.buffer.UnpooledByteBufAllocator;
import reactor.core.publisher.Flux;

@Slf4j
@EnableWebFluxSecurity
public class WebSecurityConfig {
    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        log.info("---进入WebSecurityConfig的charin方法-----------------");
        RedirectServerAuthenticationEntryPoint loginPoint = new RedirectServerAuthenticationEntryPoint("/service-a/account/index");
        http.authorizeExchange()
                .pathMatchers("/service-a/easyui/**","/service-a/js/**","/service-a/account/index","/service-a/account/login").permitAll()
                .and()
                /**
                 * 这段配置表示需要认证的请求是:
                 * /service-a/account/authen
                 * (对手正常的Springmvc 服务来说，这个应该是登陆时form的action请求地址),
                 * 如果没有认证，跳转到loginPoint设置的地址：/service-a/account/index，即登陆页面。
                 */
                .formLogin().loginPage("/service-a/account/authen")
                //请求不做验证直接跳过
                .authenticationEntryPoint(loginPoint)
                .authenticationSuccessHandler((webFilterExchange,authentication)->{//认证成功之后返回给客户端的信息
        JSONObject result = new JSONObject();
        result.put("code", 0);
        return webFilterExchange.getExchange().getResponse().writeWith(Flux.create(sink -> {
            NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(new UnpooledByteBufAllocator(false));
            try {
                DataBuffer dataBuffer= nettyDataBufferFactory.wrap(result.toJSONString().getBytes("utf8"));
                sink.next(dataBuffer);
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();

            }
            sink.complete();
        }));
    })
        //一个请求的访问权限可以被多个角色共同拥有，的权限验证
        .and().authorizeExchange().pathMatchers("/service-a/account/main").access(new XinyueReactiveAuthorizationManager("Manager", "Dev")).and().authorizeExchange().anyExchange().authenticated()
        /**
         * 这段配置表示其它请求都必须是认证（登陆成功）之后才可以访问。
         */
        .and().csrf().disable();
        SecurityWebFilterChain chain = http.build();
        Iterator<WebFilter>  weIterable = chain.getWebFilters().toIterable().iterator();
        while(weIterable.hasNext()) {
            WebFilter f = weIterable.next();
            if(f instanceof AuthenticationWebFilter) {
              AuthenticationWebFilter webFilter = (AuthenticationWebFilter) f;
              //将自定义的AuthenticationConverter添加到过滤器中
              webFilter.setServerAuthenticationConverter(new XinyueAuthenticationConverter());
            }
        }
        return chain;
    }

    /**
     * 认证成功操作
     * @return
     */
    @Bean
    public ReactiveAuthenticationManager reactiveAuthenticationManager() {
        log.info("-------进入认证成功的SecurityConfig.Reactive方法------");
        return new ReactiveAuthenticationManagerAdapter((authentication)->{
            if(authentication instanceof XinyueAccountAuthentication) {
                XinyueAccountAuthentication gmAccountAuthentication = (XinyueAccountAuthentication) authentication;
                if(gmAccountAuthentication.getPrincipal() != null) {
                    authentication.setAuthenticated(true);
                    return authentication;
                } else {
                    return authentication;
                }
            } else {
                return authentication;
            }
        });
    }
}
